
/**
 * @file:       buffer.hpp
 * @author:     WubinWang
 * @contact:    wubinstu@163.com
 * @date:       2025-01-01
 * @license:    MIT License
 *
 * Copyright (c) 2025 WubinWang
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * DISCLAIMER:
 * 1. User acknowledges all application risks and consequences arising from usage
 *    of this software are exclusively borne by the user.
 * 2. The original author shall not be held liable for any direct, indirect,
 *    incidental, special, exemplary or consequential damages.
 * 3. No warranty of fitness for particular purpose or non-infringement is provided. */


#ifndef __BUFFER_HPP_
#define __BUFFER_HPP_

#include <cstdint>

#include "byte.hpp"
#include "index.hpp"
#include "verification.hpp"

struct Frame_Config_st {

    /**
     * Section 使用说明:
     * 比如:
     * _section_data = {0x01, 0x02, 0x03, 0x04, 0x05}
     * _section_size = 2
     * _section_count = 2
     * 
     * 则表示有两种 section, 每个 section 的长度为 2, 分别是:
     * {0x01, 0x02}, {0x03, 0x04}, 而 0x05 会被忽略
     * 如果将 _section_count 修改为 3, 则还会存在一个 section:
     * {0x05, 0x??} 其中 0x?? 是缓冲区溢出的随机值 */

    struct Section_st {
        std::uint8_t _section_data[8];  // block data
        std::uint8_t _section_size;     // block size
        std::uint8_t _section_count;    // block counts
    } _m_header, _m_tail;

    struct Check_st {
        enum class Type_et : std::uint8_t {
            Check_None = 0,
            Check_Crc  = 1,
            Check_Acc  = 2,
            Check_Xor  = 3,
        } _type;

        Verification_Base_st * _calculate_instance;  // 校验计算器实例, 自动多态
        std::uint16_t          _offset;              // 被校验字节的起始位置, [对于不定长帧: 设置为 0 时表示全帧校验, 其值为 全帧长度 与 校验结果长度 的差值]
        std::uint16_t          _size_data;           // 被校验字节数, 对于不定长帧, 当 (_size_other != 0) || (_record_size_length != 0) 时将自动进行长度推断
        std::uint16_t          _size_result;         // 校验结果长度
        std::uint16_t          _size_other;          // (为 0 时不生效) 只用于不定帧长, 非校验字节数(帧的固定信息, 或者说是没有任何数据载荷的最小帧长), 用于和整帧长度做差间接得出校验字节数
        std::uint16_t          _record_size_offset;  // (为 0 时不生效) 只用于不定帧长, 帧内部记录了数据长度, 该数据域的偏移
        std::uint16_t          _record_size_length;  // (为 0 时不生效) 只用于不定帧长, 帧内部记录了数据长度, 该数据域的长度
    } _m_check;

    std::int32_t                      _m_frame_length;     // 对于带有帧头帧尾的不定长帧, 将会自动进行长度推断
    Bytes_Calculator_st::ByteOrder_et _m_fram_byte_order;  // 用于本地校验和帧内校验的比较方法 (是否需要转换字节序)


    /**
     * 对于定长帧, 你需要如实填写:
     * Frame_Config_st::(_m_header/_m_tail)
     * Frame_Config_st::Check_st::(_type/_calculate_instance/_offset/_size_data/_size_result)
     * Frame_Config_st::(_m_frame_length/_m_fram_byte_order/_m_frame_fix/_m_return)
     * 等信息, 这些信息填写后是固定的, 校验过程中这些值不会改变, 除非你手动更新了他们
     *
     *
     *
     *
     * 对于不定长帧, 你可以放弃填写:
     * Frame_Config_st::_m_frame_length         (自动推导)
     * Frame_Config_st::Check_st::_size_data    (自动推导)
     *
     * 但是可能需要填写:
     * Frame_Config_st::Check_st::(_size_other/_record_size_offset/_record_size_length)
     *
     * 关于不定长帧的动态自动推导, 校验过程中可能会修改:
     * Frame_Config_st::_m_frame_length
     * Frame_Config_st::Check_st::_size_data
     * 
     * 自动推导规则如下:
     * 1. 如果不定长帧同时设置了帧头和帧尾, 则自动根据帧头和帧尾在缓冲区中的位置推算 "整帧长度"
     * 2. 如果没有设置帧尾, 但是同时设置了 "非校验字节数" 与 "不定长数据域的长度"
     *    即不定长帧的内部存在一部分区域存储了 "不定长数据域的长度", 则 "整帧长度" 被自动推导为 "非校验字节数" 与 "不定长数据域的长度" 的和
     * 3. 上述两步完成了 "整帧长度" 的自动推导, 然后基于该结论进行 "数据长度(参与校验的字节数)" 的推导:
     * 4. 根据 "被校验字节的起始位置" == 0 判断是否是 "全帧校验", 如果是则 "被校验字节数" 被自动推导为 "整帧长度" 与 "校验结果长度" 的差
     * 5. 如果判断为非 "全帧校验" 且设置了 "非校验字节数", 则 "被校验字节数" 被自动推导为 "整帧长度" 与 "非校验字节数" 的差
     * 6. 如果判断为非 "全帧校验" 且设置了 "不定长数据域的长度", 则 "被校验字节数" 被直接推导为 "不定长数据域的长度"
     *
     *
     *
     * 注意:
     * 1. 如果没有设置帧头, 则永远无法取出完整帧
     * 2. 如果你设置了校验, 但是没有设置校验计算器实例, 则检验会自动跳过且永远无法取出完整帧
     * 3. 如果不定长帧既没有设置帧尾, 又没有设置 "非校验字节数" 与 "不定长数据域的长度", 则 "整帧长度" 无法完成自动推导, 即无法取出完整帧
     * 4. 如果某通信协议既没有帧尾, 帧内部又没有 "不定长数据域的长度", 则该通信协议应该被理解为 "有限个种类的定长帧", 通过不断 updateFrameConfig 和 getIntegralFrame 来取数 */

    enum class Fix_et : std::uint8_t {
        Fixed   = 0,
        UnFixed = 1,
    } _m_frame_fix;

    enum class Return_et : std::uint8_t {
        After_One = 0,
        After_All = 1,
    } _m_return;
};

/**
 * 简单的使用步骤:
 * 1.创建一个 Frame_Config_st 对象, 传入构造函数构造一个 Buffer_Controller_ct 对象
 *   (也可以先默认构造一个后续使用 Buffer_Controller_ct::frameConfig 重新设置配置)
 * 2.调用 Buffer_Controller_ct::push 开始传入数据
 * 3.调用 Buffer_Controller_ct::pop, 它将根据配置自动寻找帧头/帧尾, 计算校验并输出一个完整帧 */
class Buffer_Controller_ct {

  public:
    explicit Buffer_Controller_ct (const Buffer_Controller_ct & _other) noexcept    = default;
    explicit Buffer_Controller_ct (Buffer_Controller_ct && _other) noexcept         = default;
    Buffer_Controller_ct & operator= (const Buffer_Controller_ct & _other) noexcept = default;
    Buffer_Controller_ct & operator= (Buffer_Controller_ct && _other) noexcept      = default;
    explicit Buffer_Controller_ct () noexcept                                       = default;
    ~Buffer_Controller_ct () noexcept                                               = default;

    explicit Buffer_Controller_ct (const struct Frame_Config_st & _verify_config) noexcept;

    using Locker_Mutex_ft = void (*) ();

    operator std::uint8_t * () noexcept;
    auto operator+ (unsigned _offset) noexcept -> std::uint8_t *;

    auto        getBuffer () noexcept -> std::uint8_t *;     // 获取内部缓冲区
    static auto getBufferSize () noexcept -> std::uint32_t;  // 获取缓存区大小

    auto isIndexValid (std::int64_t _index) noexcept -> bool;  //判断下标是否合法

    auto getIndexBegin () const noexcept -> std::uint32_t;  // 获取读指针
    auto getIndexBeginAddr () noexcept -> std::uint8_t *;   // 获取读指针指向的字节的地址

    auto getIndexEnd () const noexcept -> std::uint32_t;  // 获取写指针
    auto getIndexEndAddr () noexcept -> std::uint8_t *;   // 获取写指针指向的字节的地址

    auto getBufferLoadSize () const noexcept -> std::uint32_t;  // 获取当前缓冲区使用的字节数
    auto getBufferFreeSize () const noexcept -> std::uint32_t;  // 获取当前缓冲区空闲的字节数

    auto isBufferFull () noexcept -> bool;   // 缓冲区是否满了
    auto isBufferEmpty () noexcept -> bool;  // 缓冲区是否空了

    auto getLock () noexcept -> Buffer_Controller_ct::Locker_Mutex_ft &;    // 获取 lock 函数指针
    auto getUnLock () noexcept -> Buffer_Controller_ct::Locker_Mutex_ft &;  // 获取 unlock 函数指针

    auto frameConfig () noexcept -> struct Frame_Config_st &;  // 返回帧格式的引用

    auto clear () noexcept -> void;                                                                                                                     // 清空缓冲区
    auto push (std::uint8_t _data[], std::uint32_t _length) noexcept -> std::uint32_t;                                                                  // 输入数据
    auto pop (std::uint8_t _data[], std::uint32_t _start = 0, std::uint32_t _max_size = Buffer_Controller_ct::_buffer_size) noexcept -> std::uint32_t;  // 输出数据 (获取一个完整帧)
    auto pop () noexcept -> bool;                                                                                                                       // 跳过当前帧

    auto findHead () noexcept -> int;                        // 找到下一个帧头
    auto nextHead (std::int64_t _relative) noexcept -> int;  // 从指定相对位置开始找到下一个帧头

    auto findTail () noexcept -> int;                        // 找到下一个帧尾
    auto nextTail (std::int64_t _relative) noexcept -> int;  // 从指定相对位置开始找到下一个帧尾


  protected:
    auto lock () noexcept -> void;
    auto unlock () noexcept -> void;

  private:
    constexpr const inline static auto && _buffer_size                                  = 4096;
    std::uint8_t                          _m_buffer[Buffer_Controller_ct::_buffer_size] = {};

    Buffer_Controller_ct::Locker_Mutex_ft _m_lock {nullptr};
    Buffer_Controller_ct::Locker_Mutex_ft _m_unlock {nullptr};
    Frame_Config_st                       _m_frame_config {};
    Advanced_Circular_Stepper_ct          _m_buffer_index_begin {0, Buffer_Controller_ct::_buffer_size};
    Advanced_Circular_Stepper_ct          _m_buffer_index_end {0, Buffer_Controller_ct::_buffer_size};
};


#endif
